/*
 * Utils.java  0.4.0 / Nov 5, 2010
 *
 * Copyright (c) 2010 - 2011, cute4j.com and contributors.
 *
 * This software is licensed under the LGPL version 2.1 license.
 * A copy of this license is included in the file LICENSE-LGPL.txt.
 */
package com.cute4j.tools.ng;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.channels.FileChannel;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;

import com.thoughtworks.qdox.model.AbstractJavaEntity;
import com.thoughtworks.qdox.model.DocletTag;
import com.thoughtworks.qdox.model.JavaClass;
import com.thoughtworks.qdox.model.JavaField;
import com.thoughtworks.qdox.model.JavaMethod;
import com.thoughtworks.qdox.model.JavaParameter;
import com.thoughtworks.qdox.model.Type;

/**
 * Helper Class.
 *
 * @author Slobodan Vrkacevic
 * @version 0.4.0
 */
public final class Utils {

// Private Static Fields. ------------------------------------------------------

  private static final SimpleDateFormat DATE_TIME_FORMAT;
  private static final SimpleDateFormat DATE_FORMAT;
  private static final Type TYPE_CLASS;
  private static final Type TYPE_THROWABLE;
  private static final Type TYPE_STRING;


// Static Initialization. ------------------------------------------------------

  static {
    DATE_TIME_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
    TYPE_CLASS = new Type("java.lang.Class");
    TYPE_THROWABLE = new Type("java.lang.Throwable");
    TYPE_STRING = new Type("java.lang.String");
  }

// Private Constructor. --------------------------------------------------------

 /**
  * Class has no instances.
  */
  private Utils() {
    // empty constructor body
  }

// Public Static Methods. ------------------------------------------------------

  /**
   * Returns current date/time as ISO formatted string.
   *
   * @return current date/time as ISO formatted string
   */
  public static String currentDateTime() {
    return DATE_TIME_FORMAT.format(new Date());
  }

  /**
   * Returns current date as ISO formatted string.
   *
   * @return current date as ISO formatted string
   */
  public static String currentDate() {
    return DATE_FORMAT.format(new Date());
  }

  /**
   * Returns JNI type for the specified java type.
   *
   * @param javaType the java type
   *
   * @return JNI type for the specified java type
   */
  public static String getJniType(Type javaType) {
    if (javaType.isVoid()) {
      return "void";
    }
    switch (javaType.getDimensions()) {
      case 0:
        if (javaType.isPrimitive()) {
          return "j" + javaType.getValue();
        }
        if (javaType.isA(TYPE_STRING)) {
          return "jstring";
        }
        if (javaType.isA(TYPE_THROWABLE)) {
          return "jthrowable";
        }
        if (javaType.isA(TYPE_CLASS)) {
          return "jclass";
        }
        return "jobject";
      case 1:
        if (javaType.isPrimitive()) {
          return "j" + javaType.getValue() + "Array";
        }
        return "jobjectArray";
      default:
        return "jobjectArray";
    }
  }

  /**
   * Returns the signature of the specified java type.
   *
   * @param javaType the java type
   * @param mangled if <tt>true</tt> the signature is mangled
   *
   * @return the signature of the specified java type
   */
  public static String getSignature(Type javaType, boolean mangled) {
    String name = javaType.getValue();
    if ("boolean".equals(name)) {
      return "Z";
    }
    if ("byte".equals(name)) {
      return "B";
    }
    if ("char".equals(name)) {
      return "C";
    }
    if ("short".equals(name)) {
      return "S";
    }
    if ("int".equals(name)) {
      return "I";
    }
    if ("long".equals(name)) {
      return "J";
    }
    if ("float".equals(name)) {
      return "F";
    }
    if ("double".equals(name)) {
      return "D";
    }
    if ("void".equals(name)) {
      return "V";
    }
    if (mangled) {
      return "L" + name.replace('.', '_') + "_2";
    }
    return "L" + name.replace('.', '/') + ";";
  }

  /**
   * Returns the signature of the specified java type.
   *
   * @param javaType the java type
   *
   * @return the signature of the specified java type
   */
  public static String getSignature(Type javaType) {
    return getSignature(javaType, false);
  }

  /**
   * Returns signature of the specified method parameters.
   *
   * @param params the method parameters
   * @param mangled if <tt>true</tt> the signature is mangled
   *
   * @return signature of the specified method parameters
   */
  public static String getSignature(JavaParameter[] params, boolean mangled) {
    StringBuffer sb = new StringBuffer();
    if (mangled) {
      for (int i = 0; i < params.length; i++) {
        if (params[i].getType().getDimensions() == 1) {
          sb.append('_'); // NOPMD
          sb.append('3'); // NOPMD
        }
        sb.append(getSignature(params[i].getType(), true));
      }
    } else {
      for (int i = 0; i < params.length; i++) {
        if (params[i].getType().getDimensions() == 1) {
          sb.append('[');
        }
        sb.append(getSignature(params[i].getType(), false));
      }
    }
    return sb.toString();
  }

  /**
   * Returns signature of the specified method parameters.
   *
   * @param params the method parameters
   *
   * @return signature of the specified method parameters
   */
  public static String getSignature(JavaParameter[] params) {
    return getSignature(params, false);
  }

  /**
   * Returns signature of the specified java method.
   *
   * @param javaMethod the java method
   * @param mangled if <tt>true</tt> the signature is mangled
   *
   * @return signature of the specified java method
   */
  public static String getSignature(JavaMethod javaMethod, boolean mangled) {
    StringBuffer sb = new StringBuffer(80);
    sb.append('(');
    sb.append(getSignature(javaMethod.getParameters(), mangled));
    sb.append(')');
    sb.append(getSignature(javaMethod.getReturns(), mangled));
    return sb.toString();
  }

  /**
   * Returns signature of the specified java method.
   *
   * @param javaMethod the java method
   *
   * @return signature of the specified java method
   */
  public static String getSignature(JavaMethod javaMethod) {
    return getSignature(javaMethod, false);
  }

  /**
   * Returns full qualified method name.
   *
   * @param javaClass the java class
   *
   * @return full qualified method name
   */
  public static String getMangledClassName(JavaClass javaClass) {
    return javaClass.getFullyQualifiedName().replace('.', '_');
  }

  /**
   * Returns mangled method name.
   *
   * @param javaMethod the java method
   *
   * @return mangled method name
   */
  public static String getMangledMethodName(JavaMethod javaMethod) {
    StringBuffer sb = new StringBuffer(100);
    sb.append(getMangledClassName(javaMethod.getParentClass()));
    sb.append('_');
    String methodName = javaMethod.getName();
    for (int i = 0; i < methodName.length(); i++) {
      char c = methodName.charAt(i);
      if (c == '_') {
        sb.append('_'); // NOPMD
        sb.append('1'); // NOPMD
      } else {
        sb.append(c);
      }
    }
    return sb.toString();
  }

  /**
   * Returns JNI method name.
   *
   * @param javaMethod the java method
   * @param overridden <tt>true</tt> if the method is overridden
   *
   * @return JNI method name
   */
  public static String getJniMethodName(JavaMethod javaMethod,
                                        boolean overridden) {
    StringBuffer sb = new StringBuffer("Java_");
    sb.append(getMangledMethodName(javaMethod));
    if (overridden) {
      sb.append('_'); // NOPMD
      sb.append('_'); // NOPMD
      sb.append(Utils.getSignature(javaMethod.getParameters(), true));
    }
    return sb.toString();
  }

  /**
   * Returns JNI constant name.
   *
   * @param javaField the java field
   *
   * @return JNI constant name
   */
  public static String getJniConstantName(JavaField javaField) {
    return getMangledClassName((JavaClass)javaField.getParent()) +
           "_" + javaField.getName();
  }

  /**
   * Returns the name of the delegate.
   *
   * @param javaClass the java class
   *
   * @return the name of the delegate
   */
  public static String getDelegateName(JavaClass javaClass) {
    String delegateName = getNativeTagParameter(javaClass, "delegate");
    if (delegateName != null) {
      return delegateName;
    }
    return javaClass.getName() + "Delegate";
  }

  /**
   * Returns the name of the delegate object.
   *
   * @param javaClass the java class
   *
   * @return the name of the delegate object
   */
  public static String getDelegateObjectName(JavaClass javaClass) {  // TODO: check usage
    String name = javaClass.getName();
    name = name.substring(0, 2).toLowerCase() + name.substring(2); //NOFB
    return name + "Delegate";
  }

  /**
   * Returns the name of the delegate object.
   *
   * @param javaClass the java class
   *
   * @return the name of the delegate object
   */
  public static String getDelegateObjectMacro(JavaClass javaClass) {
    String name = javaClass.getName();
    StringBuffer sb = new StringBuffer(2 * name.length());
    for (int i = name.length(); --i > 0;) {
      char c = name.charAt(i);
      if (Character.isUpperCase(c)) {
        sb.insert(0, c);
        sb.insert(0, '_');
      } else {
        sb.insert(0, Character.toUpperCase(c));
      }
    }
    sb.insert(0, name.charAt(0));
    sb.append("_DELEGATE");
    return sb.toString();
  }

  /**
   * Returns the name of the delegate method.
   *
   * @param javaMethod the java method
   *
   * @return the name of the delegate method
   */
  public static String getDelegateMethodName(JavaMethod javaMethod) {
    return javaMethod.getName().substring(1);
  }

  /**
   * Returns the parameters of the delegate method.
   *
   * @param javaMethod the java method
   *
   * @return the parameters of the delegate method
   */
  public static String getDelegateMethodParameters(JavaMethod javaMethod) {
    StringBuffer sb = new StringBuffer(80);
    sb.append("JNIEnv *env");
    JavaParameter[] params = javaMethod.getParameters();
    if (javaMethod.isStatic()) {
      sb.append(", jclass cls");
    }
    for (int i = 1; i < params.length; i++) {
      sb.append(','); // NOPMD
      sb.append(' '); // NOPMD
      sb.append(Utils.getJniType(params[i].getType()));
      sb.append(' '); // NOPMD
      sb.append(params[i].getName());
    }
    return sb.toString();
  }

  /**
   * Returns the documentation of the delegate method.
   *
   * @param javaMethod the java method
   *
   * @return the documentation of the delegate method
   */
  public static String getDelegateConstructorComment(JavaMethod javaMethod) {
    StringBuffer sb = new StringBuffer(160);
    sb.append("/*!");
    sb.append(NativeGenerator.INSTANCE.getNewLine());
    sb.append(" * \\brief Constructs new ");
    sb.append(Utils.getDelegateName(javaMethod.getParentClass()));
    sb.append(" object.");
    sb.append(NativeGenerator.INSTANCE.getNewLine());
    sb.append(" *");
    sb.append(NativeGenerator.INSTANCE.getNewLine());
    sb.append(" * \\param env the JNIEnv pointer");
    sb.append(NativeGenerator.INSTANCE.getNewLine());
    sb.append(" * \\param obj ");
    JavaParameter[] params = javaMethod.getParameters();
    for (int i = 0; i < params.length; i++) {
      sb.append(NativeGenerator.INSTANCE.getNewLine());
      sb.append(" * \\param ");
      sb.append(params[i].getName());
    }
    sb.append(NativeGenerator.INSTANCE.getNewLine());
    sb.append(" */");
    return sb.toString();
  }

  /**
   * Returns the declaration of the delegate constructor.
   *
   * @param javaMethod the java method
   *
   * @return the declaration of the delegate constructor
   */
  public static String getDelegateConstructorDeclaration(JavaMethod javaMethod) {
    StringBuffer sb = new StringBuffer(80);
    sb.append("  ");
    sb.append(getDelegateName(javaMethod.getParentClass()));
    sb.append("(JNIEnv *env, jobject obj");
    JavaParameter[] params = javaMethod.getParameters();
    for (int j = 0; j < params.length; j++) {
      sb.append(','); // NOPMD
      sb.append(' '); // NOPMD
      sb.append(Utils.getJniType(params[j].getType()));
      sb.append(' '); // NOPMD
      sb.append(params[j].getName());
    }
    sb.append(");");
    return sb.toString();
  }

  /**
   * Returns the declaration of the delegate constructor.
   *
   * @param javaMethod the java method
   *
   * @return the declaration of the delegate constructor
   */
  public static String getDelegateConstructorImplementation(
                                                       JavaMethod javaMethod) {
    StringBuffer sb = new StringBuffer(80);
    String delegateName = getDelegateName(javaMethod.getParentClass());
    sb.append(delegateName);
    sb.append(':'); // NOPMD
    sb.append(':'); // NOPMD
    sb.append(delegateName);
    sb.append("(JNIEnv *env, jobject obj");
    JavaParameter[] params = javaMethod.getParameters();
    for (int j = 0; j < params.length; j++) {
      sb.append(','); // NOPMD
      sb.append(' '); // NOPMD
      sb.append(Utils.getJniType(params[j].getType()));
      sb.append(' '); // NOPMD
      sb.append(params[j].getName());
    }
    sb.append(')');
    sb.append(NativeGenerator.INSTANCE.getNewLine());
    sb.append('{');
    sb.append(NativeGenerator.INSTANCE.getNewLine());
    sb.append("  // TODO: Implement");
    sb.append(NativeGenerator.INSTANCE.getNewLine());
    sb.append('}');
    sb.append(NativeGenerator.INSTANCE.getNewLine());
    return sb.toString();
  }

  /**
   * Returns the documentation of the delegate method.
   *
   * @param javaMethod the java method
   *
   * @return the documentation of the delegate method
   */
  public static String getDelegateMethodComment(JavaMethod javaMethod) {
    StringBuffer sb = new StringBuffer(256);
    sb.append("/*!");
    sb.append(NativeGenerator.INSTANCE.getNewLine());
    sb.append(" * \\brief ");
    sb.append(NativeGenerator.INSTANCE.getNewLine());
    sb.append(" *");
    sb.append(NativeGenerator.INSTANCE.getNewLine());
    sb.append(" * \\param env the JNIEnv pointer");
    int i = 1;
    if (javaMethod.isStatic()) {
      sb.append(NativeGenerator.INSTANCE.getNewLine());
      sb.append(" * \\param cls the java class");
      i = 0;
    }
    JavaParameter[] params = javaMethod.getParameters();
    for (; i < params.length; i++) {
      sb.append(NativeGenerator.INSTANCE.getNewLine());
      sb.append(" * \\param ");
      sb.append(params[i].getName());
      sb.append(' ');
    }
    sb.append(NativeGenerator.INSTANCE.getNewLine());
    if (!javaMethod.getReturns().isVoid()) {
      sb.append(" * ");
      sb.append(NativeGenerator.INSTANCE.getNewLine());
      sb.append(" * \\return ");
      sb.append(NativeGenerator.INSTANCE.getNewLine());
    }
    sb.append(" */");
    return sb.toString();
  }

  /**
   * Returns the declaration of the delegate method.
   *
   * @param javaMethod the java method
   *
   * @return the declaration of the delegate method
   */
  public static String getNativeCallMethodDeclaration(JavaMethod javaMethod) {
    StringBuffer sb = new StringBuffer(80);
    sb.append("  ");
    sb.append(getJniType(javaMethod.getReturns()));
    sb.append(' ');
    sb.append(getDelegateMethodName(javaMethod));
    sb.append('(');
    sb.append(getDelegateMethodParameters(javaMethod));
    sb.append(");");
    return formatMethodDeclaration(sb.toString());
  }

  /**
   * Returns the implementation of the delegate method.
   *
   * @param javaMethod the java method
   *
   * @return the implementation of the delegate method
   */
  public static String getDelegateMethodImplementation(JavaMethod javaMethod) {
    StringBuffer sb = new StringBuffer(80);
    sb.append(getJniType(javaMethod.getReturns()));
    sb.append(' ');
    sb.append(Utils.getDelegateName(javaMethod.getParentClass()));
    sb.append("::");
    sb.append(getDelegateMethodName(javaMethod));
    sb.append('(');
    if (javaMethod.isStatic()) {
      sb.append(getNativeCallMethodParameters(javaMethod));
    } else {
      sb.append(getDelegateMethodParameters(javaMethod));
    }
    sb.append(')');
    sb.append(NativeGenerator.INSTANCE.getNewLine());
    sb.append('{');
    if (!javaMethod.isStatic()) {
      sb.append(NativeGenerator.INSTANCE.getNewLine());
      sb.append("  Q_ASSERT(env);");
      JavaParameter[] params = javaMethod.getParameters();
      for (int i = 1; i < params.length; i++) {
        if (!params[i].getType().isPrimitive()) {
          sb.append(NativeGenerator.INSTANCE.getNewLine());
          sb.append("  Q_ASSERT(");
          sb.append(params[i].getName());
          sb.append(");");
        }
      }
      sb.append(NativeGenerator.INSTANCE.getNewLine());
      if (javaMethod.getReturns().isVoid()) {
        sb.append("  CUTE4J_SWING_REENTRANT(");
        sb.append(getDelegateMethodName(javaMethod));
      } else {
        sb.append("  CUTE4J_SWING_REENTRANT_RETURN(");
        sb.append(getDelegateMethodName(javaMethod));
        sb.append(','); // NOPMD
        sb.append(' '); // NOPMD
        sb.append(Utils.getJniType(javaMethod.getReturns()));
      }
      sb.append(", CUTE4J_SWING_ENV_ARG");
      params = javaMethod.getParameters();
      for (int i = 1; i < params.length; i++) {
        sb.append(','); // NOPMD
        sb.append(' '); // NOPMD
        sb.append(Utils.getJniType(params[i].getType()).toUpperCase());
        sb.append("_ARG("); // NOPMD
        sb.append(params[i].getName());
        sb.append(')'); // NOPMD
      }
      sb.append(')');
    }
    sb.append(NativeGenerator.INSTANCE.getNewLine());
    sb.append("  // TODO: Implement");
    sb.append(NativeGenerator.INSTANCE.getNewLine());
    if (!javaMethod.getReturns().isVoid()) {
      sb.append("  return 0;");
      sb.append(NativeGenerator.INSTANCE.getNewLine());
    }
    sb.append('}');
    sb.append(NativeGenerator.INSTANCE.getNewLine());
    return formatMethodImplementation(sb.toString());
  }

  /**
   * Returns the parameters of the delegate callback method.
   *
   * @param javaMethod the java method
   *
   * @return the parameters of the delegate call-back method
   */
  public static String getCallbackMethodParameters(JavaMethod javaMethod) {
    StringBuffer sb = new StringBuffer(80);
    JavaParameter[] params = javaMethod.getParameters();
    for (int i = 0; i < params.length; i++) {
      if (i > 0) {
        sb.append(','); // NOPMD
        sb.append(' '); // NOPMD
      }
      sb.append(Utils.getJniType(params[i].getType()));
      sb.append(' '); // NOPMD
      sb.append(params[i].getName());
    }
    return sb.toString();
  }

  /**
   * Returns the declaration of the delegate callback method.
   *
   * @param javaMethod the java method
   *
   * @return the declaration of the delegate method
   */
  public static String getCallbackMethodDeclaration(JavaMethod javaMethod) {
    StringBuffer sb = new StringBuffer(80);
    sb.append("  ");
    sb.append(getJniType(javaMethod.getReturns()));
    sb.append(' ');
    sb.append(getDelegateMethodName(javaMethod));
    sb.append('(');
    sb.append(getCallbackMethodParameters(javaMethod));
    sb.append(");"); //$NON-NLS-1$
    return formatMethodDeclaration(sb.toString());
  }

  /**
   * Returns the implementation of the delegate callback method.
   *
   * @param javaMethod the java method
   *
   * @return the implementation of the delegate method
   */
  public static String getCallbackMethodImplementation(JavaMethod javaMethod) {
    StringBuffer sb = new StringBuffer(80);
    sb.append(getJniType(javaMethod.getReturns()));
    sb.append(' ');
    sb.append(Utils.getDelegateName(javaMethod.getParentClass()));
    sb.append("::");
    sb.append(getDelegateMethodName(javaMethod));
    sb.append('(');
    sb.append(getCallbackMethodParameters(javaMethod));
    sb.append(')');
    sb.append(NativeGenerator.INSTANCE.getNewLine());
    sb.append('{');
    sb.append(NativeGenerator.INSTANCE.getNewLine());
    sb.append("  // TODO: Implement");
    sb.append(NativeGenerator.INSTANCE.getNewLine());
    if (!javaMethod.getReturns().isVoid()) {
      sb.append("  return 0;");
      sb.append(NativeGenerator.INSTANCE.getNewLine());
    }
    sb.append('}');
    sb.append(NativeGenerator.INSTANCE.getNewLine());
    // Utils.getNativeTagParameter(method, "code") // TODO: ???
    return formatMethodImplementation(sb.toString());
  }

  /**
   * Returns the parameters of the delegate native call method.
   *
   * @param javaMethod the java method
   *
   * @return the parameters of the delegate call-back method
   */
  public static String getNativeCallMethodParameters(JavaMethod javaMethod) {
    StringBuffer sb = new StringBuffer(80);
    int startParamIndex = 1;
    sb.append("JNIEnv *env");
    if (javaMethod.isStatic()) {
      startParamIndex = 0;
      sb.append(", jclass cls");
    } else if ("_create".equals(javaMethod.getName())) {
      sb.append(", jobject obj");
    }
    JavaParameter[] params = javaMethod.getParameters();
    for (int i = startParamIndex; i < params.length; i++) {
      sb.append(','); // NOPMD
      sb.append(' '); // NOPMD
      sb.append(Utils.getJniType(params[i].getType()));
      sb.append(' '); // NOPMD
      sb.append(params[i].getName());
    }
    return sb.toString();
  }

  /**
   * Returns the declaration of the delegate method.
   *
   * @param javaMethod the java method
   *
   * @return the declaration of the delegate method
   */
  public static String getNativeCallStaticMethodDeclaration(
                                                       JavaMethod javaMethod) {
    StringBuffer sb = new StringBuffer(80);
    sb.append("  static ");
    sb.append(getJniType(javaMethod.getReturns()));
    sb.append(' ');
    sb.append(getDelegateMethodName(javaMethod));
    sb.append('(');
    sb.append(getNativeCallMethodParameters(javaMethod));
    sb.append(");");
    return formatMethodDeclaration(sb.toString());
  }

  /**
   * Returns the documentation of the newInstance method.
   *
   * @param javaMethod the java method
   *
   * @return the documentation of the delegate method
   */
  public static String getNativeCallNewInstanceMethodComment(
                                                       JavaMethod javaMethod) {
    StringBuffer sb = new StringBuffer(256);
    sb.append("/*!");
    sb.append(NativeGenerator.INSTANCE.getNewLine());
    sb.append(" * \\brief Returns new instance of the delegate object");
    sb.append(NativeGenerator.INSTANCE.getNewLine());
    sb.append(" *");
    sb.append(NativeGenerator.INSTANCE.getNewLine());
    sb.append(" * \\param env the JNIEnv pointer");
    sb.append(NativeGenerator.INSTANCE.getNewLine());
    sb.append(" * \\param obj the Swing object");
    sb.append(NativeGenerator.INSTANCE.getNewLine());
    sb.append(" * ");
    sb.append(NativeGenerator.INSTANCE.getNewLine());
    sb.append(" * \\return new instance of the delegate object");
    sb.append(NativeGenerator.INSTANCE.getNewLine());
    sb.append(" */");
    return sb.toString();
  }

  /**
   * Returns the declaration of the newInstance method.
   *
   * @param javaMethod the java method
   *
   * @return the declaration of the delegate method
   */
  public static String getNativeCallNewInstanceMethodDeclaration(
                                                       JavaMethod javaMethod) {
    StringBuffer sb = new StringBuffer(80);
    sb.append("  static ");
    sb.append(getJniType(javaMethod.getReturns()));
    sb.append(" newInstance(JNIEnv *env, jobject obj);");
    return formatMethodDeclaration(sb.toString());
  }

  /**
   * Returns the implementation of the newInstance method.
   *
   * @param javaMethod the java method
   *
   * @return the implementation of the delegate method
   */
  public static String getNativeCallNewInstanceMethodImplementation(
                                                       JavaMethod javaMethod) {
     StringBuffer sb = new StringBuffer(256);
     sb.append(getJniType(javaMethod.getReturns()));
     sb.append(' ');
     sb.append(Utils.getDelegateName(javaMethod.getParentClass()));
     sb.append("::newInstance(JNIEnv *env, jobject obj)");
     sb.append(NativeGenerator.INSTANCE.getNewLine());
     sb.append('{');
     sb.append(NativeGenerator.INSTANCE.getNewLine());
     sb.append("  if (JNISwingWrapper::isSwingThread(env)) {");
     sb.append(NativeGenerator.INSTANCE.getNewLine());
     sb.append("    return reinterpret_cast<jlong>(new ");
     sb.append(Utils.getDelegateName(javaMethod.getParentClass()));
     sb.append("(env, obj));");
     sb.append(NativeGenerator.INSTANCE.getNewLine());
     sb.append("  }");
     sb.append(NativeGenerator.INSTANCE.getNewLine());
     sb.append("  return SwingUtilitiesDelegate::" +
               "invokeLongAndWait(env, obj, createMID());");
     sb.append(NativeGenerator.INSTANCE.getNewLine());
     sb.append('}');
     sb.append(NativeGenerator.INSTANCE.getNewLine());
     return sb.toString();
  }

  /**
   * Returns the value of the specified parameter of the "cute4j.native" tag.
   *
   * @param javaEntity the abstract java entity
   * @param parameter the parameter
   *
   * @return the value of the specified parameter of the "cute4j.native" tag
   */
  public static String getNativeTagParameter(AbstractJavaEntity javaEntity,
                                             String parameter) {
    DocletTag tag = javaEntity.getTagByName("cute4j.native");
    if (tag != null) {
      String paramValue = tag.getNamedParameter(parameter);
      if (paramValue != null) {
        return paramValue;
      }
    }
    return null;
  }

  /**
   * Returns <tt>true</tt> if the code contains the specified parameter.
   *
   * @param code the code
   * @param param the parameter
   *
   * @return <tt>true</tt> if the code contains the specified parameter
   */
  public static boolean parameterUsed(String code, String param) {
    // TODO: implement better
    return ((code == null) || ("delegate".equals(param)) ||
            (code.indexOf(param) != -1));
  }

  /**
   * Print block comment.
   *
   * @param pw the print writer
   * @param comment the comment
   *
   * @throws IOException on an I/O error
   */
  public static void printBlockComment(PrintWriter pw, String comment)
                                                           throws IOException {
    pw.print("// ");
    pw.print(comment);
    pw.print(" ");
    for (int i = 75 - comment.length(); i >= 0; i--) {
      pw.print("-");
    }
    pw.println();
    pw.println();
  }

  /**
   * Formats method declaration string.
   *
   * @param methodDeclaration the declaration string
   *
   * @return formatted method declaration string
   */
  public static String formatMethodDeclaration(String methodDeclaration) {
    if (methodDeclaration.length() > 79) {
      int bracketPosition = methodDeclaration.indexOf('(');
      String newLine = NativeGenerator.getInstance().getNewLine();
      char[] spaces = new char[bracketPosition + newLine.length()];
      for (int i = spaces.length - 1; i >= newLine.length(); i--) {
        spaces[i] = ' ';
      }
      for (int i = 0, j = 0; j < newLine.length(); i++, j++) {
        spaces[i] = newLine.charAt(j);
      }
      StringBuffer sb = new StringBuffer(methodDeclaration);
      int i = bracketPosition;
      int inLinePostion = i;
      int lastComma = i;
      while (i < sb.length()) {
        if (inLinePostion > 79) {
          sb.insert(lastComma + 1, spaces);
          i += spaces.length - newLine.length();
          inLinePostion = bracketPosition;
        } else {
          if (sb.charAt(i) == ',') {
            lastComma = i;
          }
          i++;
          inLinePostion++;
        }
      }
      return sb.toString();
    }
    return methodDeclaration;
  }

  /**
   * Formats method declaration string.
   *
   * @param methodDeclaration the declaration string
   *
   * @return formatted method declaration string
   */
  public static String formatMethodImplementation(String methodDeclaration) {
    String newLine = NativeGenerator.getInstance().getNewLine();
    int nli = methodDeclaration.indexOf(newLine);
    nli = nli + newLine.length();
    String methodName = methodDeclaration.substring(0, nli);
    String methodBody = methodDeclaration.substring(nli);
    return formatMethodDeclaration(methodName) + methodBody;
  }

  /**
   * Returns index of method implementation.
   *
   * @param methods the methods the methods
   * @param methodImplementation the method implementation
   *
   * @return index of method implementation
   */
  public static int getMethodImplementationIndex(ArrayList methods,
                                                 String methodImplementation) {
    String newLine = NativeGenerator.getInstance().getNewLine();
    String commMethodEnd = "*/" + newLine;
    for (int i = 0; i < methods.size(); i++) {
      String methodText = (String)methods.get(i);
      if (!methodText.endsWith(commMethodEnd)) {
        int cpi = methodImplementation.indexOf(')') + 1;
        if (methodText.indexOf(methodImplementation.substring(0, cpi)) >= 0) {
          return i;
        }
      }
    }
    return -1;
  }

  /**
   * Back up the specified file.
   *
   * @param file the file to back up
   *
   * @throws IOException on an I/O error
   */
  public static void backupFile(File file) throws IOException {
    FileChannel fileChannel = null;
    FileChannel backupChannel = null;
    try {
      File backupFile = new File(file.getAbsolutePath() + "~");
      fileChannel = new FileInputStream(file).getChannel();
      backupChannel = new FileOutputStream(backupFile).getChannel();
      backupChannel.transferFrom(fileChannel, 0, fileChannel.size());
    } finally {
      if (fileChannel != null) {
        fileChannel.close();
      }
      if (backupChannel != null) {
        backupChannel.close();
      }
    }
  }

}
